﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using EX_NFC.FrontEnd.Windows.Properties;
using Equinoxe.EZNFC.Library;
using Equinoxe.EZNFC.Library.Tags;
using Equinoxe.EZNFC.Library.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;

namespace EX_NFC.FrontEnd.Windows.ViewModel
{
    // ReSharper disable InconsistentNaming
    /// <summary>
    /// Mifare 1K tag viewmodel
    /// </summary>
    /// <remarks></remarks>
    public class Mifare1KVM : ViewModelBase
    // ReSharper restore InconsistentNaming
    {

        #region Properties

        /// <summary>
        /// Tag backing field.
        /// </summary>
        private Mifare1K _tag;
        /// <summary>
        /// Gets or sets the tag.
        /// </summary>
        /// <value>The tag.</value>
        /// <remarks></remarks>
        public Mifare1K Tag
        {
            get { return _tag; }
            set
            {
                _tag = value;
                RaisePropertyChanged("Tag");
                RaisePropertyChanged("Key");
                RaisePropertyChanged("SectorList");
                RaisePropertyChanged("BlockList");
                RaisePropertyChanged("KeyList");
                RaisePropertyChanged("SelectedKey");
                RaisePropertyChanged("ByteKey");
                RaisePropertyChanged("SelectedSector");
                RaisePropertyChanged("SelectedBlock");
                RaisePropertyChanged("BlockValue");
                RaisePropertyChanged("BlockValueIncrement");
                RaisePropertyChanged("ByteBlock");
                RaisePropertyChanged("BlockDataString");
                RaisePropertyChanged("DestinationSector");
                RaisePropertyChanged("DestinationBlock");
                RaisePropertyChanged("DestinationBlockList");
                RaisePropertyChanged("TrailerBlockAuthorizationLines");
                RaisePropertyChanged("DataBlockAuthorizationLines");
                RaisePropertyChanged("Block0Access");
                RaisePropertyChanged("Block1Access");
                RaisePropertyChanged("Block2Access");
                RaisePropertyChanged("TrailerBlockAccess");

                Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagStatus, Message = "MiFare 1K" }),MessageType.StatusMessage);
            }
        }

        /// <summary>
        /// Gets or sets the key.
        /// </summary>
        /// <value>The key.</value>
        /// <remarks></remarks>
        public string Key { get; set; }

        /// <summary>
        /// Gets the sector list.
        /// </summary>
        /// <remarks></remarks>
        public IEnumerable<int> SectorList
        {
            get
            {
                if ((Tag == null) || (Tag.TagInfo == null))
                    yield break;

                for (var i = 0; i < Tag.TagInfo.NumberOfSectors; i++)
                {
                    yield return i;
                }
            }
        }

        /// <summary>
        /// Gets the block list.
        /// </summary>
        /// <remarks></remarks>
        public IEnumerable<int> BlockList
        {
            get
            {
                if ((Tag == null) || (Tag.TagInfo == null))
                    yield break;

                for (var i = 0; i < Tag.TagInfo.GetNumberOfBlocksForSector(SelectedSector); i++)
                {
                    yield return i;
                }
            }
        }

        /// <summary>
        /// Gets the key list.
        /// </summary>
        /// <remarks></remarks>
        public IEnumerable<string> KeyList
        {
            get
            {
                if ((Tag == null) || (Tag.TagInfo == null))
                    yield break;
                //todo: check
                //const int charcode = 65;

                const char charcode = 'A';

                for (var i = 0; i < Tag.TagInfo.NumberOfSectors; i++)
                {
                    yield return string.Format(Resources.TagAuthentication_KeySelection, i, Convert.ToChar(charcode + i));

                }
            }
        }

        /// <summary>
        /// Gets or sets the selected key.
        /// </summary>
        /// <value>The selected key.</value>
        /// <remarks></remarks>
        public int SelectedKey { get; set; }

        /// <summary>
        /// ByteKey backing field.
        /// </summary>
        private bool _byteKey = true;
        /// <summary>
        /// Gets or sets a value indicating whether the key is entered in byte or string.
        /// </summary>
        /// <value><c>true</c> if in byte; otherwise, <c>false</c>.</value>
        /// <remarks></remarks>
        public bool ByteKey
        {
            get { return _byteKey = true; }
            set { _byteKey = value; }
        }

        /// <summary>
        /// Gets the access bit in string mode.
        /// </summary>
        /// <remarks></remarks>
        public string AccessBitInStringMode
        {
            get
            {
                return string.Format(Resources.Mifare1KAuthorization_AuthorizationBytes, _blockData != null ? BitConverter.ToString(_blockData) : "");

            }
        }

        /// <summary>
        /// SelectedSector backing field.
        /// </summary>
        private int _selectedSector;

        /// <summary>
        /// Gets or sets the selected sector.
        /// </summary>
        /// <value>The selected sector.</value>
        /// <remarks></remarks>
        public int SelectedSector
        {
            get { return _selectedSector; }
            set
            {
                _selectedSector = value;
                RaisePropertyChanged("BlockList");

            }
        }

        /// <summary>
        /// Gets or sets the selected block.
        /// </summary>
        /// <value>The selected block.</value>
        /// <remarks></remarks>
        public int SelectedBlock { get; set; }


        /// <summary>
        /// BlockValue backing field.
        /// </summary>
        private int _blockValue;
        /// <summary>
        /// Gets or sets the block value.
        /// </summary>
        /// <value>The block value.</value>
        /// <remarks></remarks>
        public int BlockValue
        {
            get { return _blockValue; }
            set { _blockValue = value; RaisePropertyChanged("BlockValue"); }
        }

        /// <summary>
        /// Gets or sets the block value increment.
        /// </summary>
        /// <value>The block value increment.</value>
        /// <remarks></remarks>
        public int BlockValueIncrement { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the block is entered in byte or string.
        /// </summary>
        /// <value><c>true</c> if in byte; otherwise, <c>false</c>.</value>
        /// <remarks></remarks>
        public bool ByteBlock { get; set; }


        /// <summary>
        /// Block data bytes.
        /// </summary>
        private byte[] _blockData;

        /// <summary>
        /// Gets or sets the block data as string.
        /// </summary>
        /// <value>The block data string.</value>
        /// <remarks></remarks>
        public string BlockDataString
        {
            get
            {
                var datapart = new byte[16];

                for (int i = 0; i < 16; i++)
                {
                    datapart[i] = _blockData[i + 3];
                }

                var s = string.Empty;
                s = ByteBlock ? datapart.Aggregate(s, (current, b) => current + string.Format("{0:X2} ", b)) : Encoding.Unicode.GetString(datapart);

                return s;
            }
            set
            {
                _blockData = new byte[16];
                if (ByteBlock)
                {
                    for (var i = 0; i < value.Length; i++)
                    {
                        _blockData[i] = byte.Parse(value.Substring((i * 2), 2), NumberStyles.HexNumber);
                    }
                }
                else
                {
                    var s = value;
                    var i = s.Length;

                    while (i < 16)
                    {
                        s += ' ';
                        i += 2;
                    }

                    _blockData = Encoding.Unicode.GetBytes(s.Substring(0,8));

                }
            }
        }


        /// <summary>
        /// DestinationSector backing field.
        /// </summary>
        private int _destinationSector;

        /// <summary>
        /// Gets or sets the destination sector.
        /// </summary>
        /// <value>The destination sector.</value>
        /// <remarks></remarks>
        public int DestinationSector
        {
            get { return _destinationSector; }
            set
            {
                _destinationSector = value;
                RaisePropertyChanged("DestinationBlockList");

            }
        }

        /// <summary>
        /// Gets or sets the destination block.
        /// </summary>
        /// <value>The destination block.</value>
        /// <remarks></remarks>
        public int DestinationBlock { get; set; }

        /// <summary>
        /// Gets the destination block list.
        /// </summary>
        /// <remarks></remarks>
        public IEnumerable<int> DestinationBlockList
        {
            get
            {
                if ((Tag == null) || (Tag.TagInfo == null))
                    yield break;

                for (var i = 0; i < Tag.TagInfo.GetNumberOfBlocksForSector(DestinationSector); i++)
                {
                    yield return i;
                }
            }
        }


        /// <summary>
        /// Gets the trailer block authorization lines.
        /// </summary>
        /// <remarks></remarks>
        public IEnumerable<TrailerBlockAuthorizationLine> TrailerBlockAuthorizationLines
        {
            get
            {
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyA,
                        AccessBitWriteAccess = Resources.Never,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.KeyA,
                        KeyBReadAccess = Resources.KeyA,
                        KeyBWriteAccess = Resources.KeyA,
                        Remark = Resources.Mifare1K_Remark1
                    };
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyA,
                        AccessBitWriteAccess = Resources.Never,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.Never,
                        KeyBReadAccess = Resources.KeyA,
                        KeyBWriteAccess = Resources.Never,
                        Remark = Resources.Mifare1K_Remark1
                    };
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyAB,
                        AccessBitWriteAccess = Resources.Never,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.KeyB,
                        KeyBReadAccess = Resources.Never,
                        KeyBWriteAccess = Resources.KeyB
                    };
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyAB,
                        AccessBitWriteAccess = Resources.Never,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.Never,
                        KeyBReadAccess = Resources.Never,
                        KeyBWriteAccess = Resources.Never
                    };
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyA,
                        AccessBitWriteAccess = Resources.KeyA,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.KeyA,
                        KeyBReadAccess = Resources.KeyA,
                        KeyBWriteAccess = Resources.KeyA,
                        Remark = Resources.Mifare1K_Remark2
                    };
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyAB,
                        AccessBitWriteAccess = Resources.KeyB,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.KeyB,
                        KeyBReadAccess = Resources.Never,
                        KeyBWriteAccess = Resources.KeyB
                    };
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyAB,
                        AccessBitWriteAccess = Resources.KeyB,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.Never,
                        KeyBReadAccess = Resources.Never,
                        KeyBWriteAccess = Resources.Never
                    };
                yield return
                    new TrailerBlockAuthorizationLine
                    {
                        AccessBitReadAccess = Resources.KeyAB,
                        AccessBitWriteAccess = Resources.Never,
                        KeyAReadAccess = Resources.Never,
                        KeyAWriteAccess = Resources.Never,
                        KeyBReadAccess = Resources.Never,
                        KeyBWriteAccess = Resources.Never
                    };
            }
        }


        /// <summary>
        /// Gets the data block authorization lines.
        /// </summary>
        /// <remarks></remarks>
        public IEnumerable<DataBlockAuthorizationLine> DataBlockAuthorizationLines
        {
            get
            {
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App1,
                        DecrementAccess = Resources.KeyAB,
                        IncrementAccess = Resources.KeyAB,
                        ReadAccess = Resources.KeyAB,
                        WriteAccess = Resources.KeyAB
                    };
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App2,
                        DecrementAccess = Resources.Never,
                        IncrementAccess = Resources.Never,
                        ReadAccess = Resources.KeyAB,
                        WriteAccess = Resources.Never
                    };
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App2,
                        DecrementAccess = Resources.Never,
                        IncrementAccess = Resources.Never,
                        ReadAccess = Resources.KeyAB,
                        WriteAccess = Resources.KeyB
                    };
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App3,
                        DecrementAccess = Resources.KeyAB,
                        IncrementAccess = Resources.KeyB,
                        ReadAccess = Resources.KeyAB,
                        WriteAccess = Resources.KeyB
                    };
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App3,
                        DecrementAccess = Resources.KeyAB,
                        IncrementAccess = Resources.Never,
                        ReadAccess = Resources.KeyAB,
                        WriteAccess = Resources.Never
                    };
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App2,
                        DecrementAccess = Resources.Never,
                        IncrementAccess = Resources.Never,
                        ReadAccess = Resources.KeyB,
                        WriteAccess = Resources.KeyB
                    };
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App2,
                        DecrementAccess = Resources.Never,
                        IncrementAccess = Resources.Never,
                        ReadAccess = Resources.KeyB,
                        WriteAccess = Resources.Never
                    };
                yield return
                    new DataBlockAuthorizationLine
                    {
                        Application = Resources.Mifare1K_App2,
                        DecrementAccess = Resources.Never,
                        IncrementAccess = Resources.Never,
                        ReadAccess = Resources.Never,
                        WriteAccess = Resources.Never
                    };
            }
        }

        /// <summary>
        /// Block0Access backing field.
        /// </summary>
        private int _block0Access;
        /// <summary>
        /// Gets or sets the block 0 access.
        /// </summary>
        /// <value>The block0 access.</value>
        /// <remarks></remarks>
        public int Block0Access
        {
            get { return _block0Access; }
            set { _block0Access = value; RaisePropertyChanged("Block0Access"); }
        }

        /// <summary>
        /// Block1Access backing field.
        /// </summary>
        private int _block1Access;
        /// <summary>
        /// Gets or sets the block 1 access.
        /// </summary>
        /// <value>The block1 access.</value>
        /// <remarks></remarks>
        public int Block1Access
        {
            get { return _block1Access; }
            set { _block1Access = value; RaisePropertyChanged("Block1Access"); }
        }

        /// <summary>
        /// _block2Access backing field.
        /// </summary>
        private int _block2Access;
        /// <summary>
        /// Gets or sets the block 2 access.
        /// </summary>
        /// <value>The block2 access.</value>
        /// <remarks></remarks>
        public int Block2Access
        {
            get { return _block2Access; }
            set { _block2Access = value; RaisePropertyChanged("Block2Access"); }
        }

        /// <summary>
        /// TrailerBlockAccess backing field.
        /// </summary>
        private int _trailerBlockAccess;
        /// <summary>
        /// Gets or sets the trailer block access.
        /// </summary>
        /// <value>The trailer block access.</value>
        /// <remarks></remarks>
        public int TrailerBlockAccess
        {
            get { return _trailerBlockAccess; }
            set { _trailerBlockAccess = value; RaisePropertyChanged("TrailerBlockAccess"); }
        }

        #endregion


        #region Commands

        /// <summary>
        /// CommandAuthenticate backing field.
        /// </summary>
        private RelayCommand _commandAuthenticate;

        /// <summary>
        /// Gets the command authenticate.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandAuthenticate
        {
            get
            {
                return _commandAuthenticate ?? (_commandAuthenticate = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_Authenticated }), MessageType.StatusMessage);
                        Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                    }
                }));
            }
        }

        /// <summary>
        /// CommandGetValue backing field.
        /// </summary>
        private RelayCommand _commandGetValue;

        /// <summary>
        /// Gets the command get value.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandGetValue
        {
            get
            {
                return _commandGetValue ?? (_commandGetValue = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        try
                        {
                            BlockValue = Tag.ReadValueBlock(SelectedSector, SelectedBlock);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_GetValue }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }

        /// <summary>
        /// CommandSetValue backing field.
        /// </summary>
        private RelayCommand _commandSetValue;

        /// <summary>
        /// Gets the command set value.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandSetValue
        {
            get
            {
                return _commandSetValue ?? (_commandSetValue = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        try
                        {
                            Tag.WriteValueBlock(SelectedSector, SelectedBlock, BlockValue, Tag.TagInfo.GetBlockNumber(SelectedSector, SelectedBlock));
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_SetValue }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }

        /// <summary>
        /// CommandIncValue backing field.
        /// </summary>
        private RelayCommand _commandIncValue;

        /// <summary>
        /// Gets the command inc value.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandIncValue
        {
            get
            {
                return _commandIncValue ?? (_commandIncValue = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        try
                        {
                            Tag.IncrementValueBlock(SelectedSector, SelectedBlock, BlockValueIncrement);
                            Tag.CommitBlockOperation(SelectedSector,SelectedBlock);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_IncrementValue }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }

        /// <summary>
        /// CommandDecValue backing field.
        /// </summary>
        private RelayCommand _commandDecValue;

        /// <summary>
        /// Gets the command dec value.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandDecValue
        {
            get
            {
                return _commandDecValue ?? (_commandDecValue = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        try
                        {
                            Tag.DecrementValueBlock(SelectedSector, SelectedBlock, BlockValueIncrement);
                            Tag.CommitBlockOperation(SelectedSector, SelectedBlock);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_DecrementValue }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }

        /// <summary>
        /// CommandGetBlock backing field.
        /// </summary>
        private RelayCommand _commandGetBlock;

        /// <summary>
        /// Gets the command get block.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandGetBlock
        {
            get
            {
                return _commandGetBlock ?? (_commandGetBlock = new RelayCommand(() =>
                {
                    var bufferlength = 255;
                    if (Authenticate())
                    {
                        try
                        {
                            _blockData = new byte[bufferlength];
                            Tag.ReadBlock(SelectedSector, SelectedBlock, _blockData, ref bufferlength);
                            RaisePropertyChanged("BlockDataString");
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_GetBlock }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }

                    }
                }));
            }
        }

        /// <summary>
        /// CommandSetBlock backing field.
        /// </summary>
        private RelayCommand _commandSetBlock;

        /// <summary>
        /// Gets the command set block.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandSetBlock
        {
            get
            {
                return _commandSetBlock ?? (_commandSetBlock = new RelayCommand(() =>
                {
                    var bufferlength = _blockData.Length;
                    if (Authenticate())
                    {
                        try
                        {
                            Tag.WriteBlock(SelectedSector, SelectedBlock, _blockData, ref bufferlength);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_SetBlock }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }

        /// <summary>
        /// CommandCopyBlock backing field.
        /// </summary>
        private RelayCommand _commandCopyBlock;

        /// <summary>
        /// Gets the command copy block.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandCopyBlock
        {
            get
            {
                return _commandCopyBlock ?? (_commandCopyBlock = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        try
                        {
                            Tag.CopyValueBlock(SelectedSector, SelectedBlock, DestinationSector, DestinationBlock);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_CopyBlock }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }



        /// <summary>
        /// CommandGetAuthorizationBytes backing field.
        /// </summary>
        private RelayCommand _commandGetAuthorizationBytes;

        /// <summary>
        /// Gets the command get authorization bytes.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandGetAuthorizationBytes
        {
            get
            {
                return _commandGetAuthorizationBytes ?? (_commandGetAuthorizationBytes = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        try
                        {
                            SectorAccessConditionsToIndex(Tag.GetBlockAccess(SelectedSector));
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_GetAccessBit }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }


        /// <summary>
        /// CommandSetAuthorizationBytes backing field.
        /// </summary>
        private RelayCommand _commandSetAuthorizationBytes;

        /// <summary>
        /// Gets the command set authorization bytes.
        /// </summary>
        /// <remarks></remarks>
        public RelayCommand CommandSetAuthorizationBytes
        {
            get
            {
                return _commandSetAuthorizationBytes ?? (_commandSetAuthorizationBytes = new RelayCommand(() =>
                {
                    if (Authenticate())
                    {
                        try
                        {
                            Tag.SetBlockAccess(SelectedSector, IndexToSectorAccessConditions());
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.TagOperationStatus, Message = Resources.TagOperationStatus_SetAccessBit }), MessageType.StatusMessage);
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
                        }
                        catch (TagException e)
                        {
                            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                        }
                    }
                }));
            }
        }

        #endregion

        /// <summary>
        /// Authenticates this instance.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        private bool Authenticate()
        {
            //TODO: add error
            if (string.IsNullOrEmpty(Key))
            {
                Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = Resources.Error_NoKey }), MessageType.StatusMessage);
                return false;
            }


            try
            {
                if (ByteKey)
                    Tag.Authenticate((AuthenticatingKey)SelectedKey, SelectedSector, SelectedBlock, Key);
                else
                    Tag.Authenticate((AuthenticatingKey)SelectedKey, SelectedSector, SelectedBlock, Encoding.Unicode.GetBytes(Key), Tag.TagInfo.KeySize);
            }
            catch (TagException e)
            {
                Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.ErrorStatus, Message = e.Message }), MessageType.StatusMessage);
                return false;
            }
            Messenger.Default.Send(new GenericMessage<StatusData>(this, new StatusData { StatusType = StatusType.NoErrorStatus }), MessageType.StatusMessage);
            return true;
        }

        /// <summary>
        /// Index number to data block access.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private Mifare1KDataBlockAccessConditions IndexToDataBlockAccess(int index)
        {
            switch (index)
            {
                case 0:
                    return Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB;
                case 1:
                    return Mifare1KDataBlockAccessConditions.rAB;
                case 2:
                    return Mifare1KDataBlockAccessConditions.rAB_wB;
                case 3:
                    return Mifare1KDataBlockAccessConditions.rAB_wB_iB_drtAB;
                case 4:
                    return Mifare1KDataBlockAccessConditions.rAB_dtrAB;
                case 5:
                    return Mifare1KDataBlockAccessConditions.rB_wB;
                case 6:
                    return Mifare1KDataBlockAccessConditions.rB;
                default:
                    return Mifare1KDataBlockAccessConditions.Nothing;
            }
        }

        /// <summary>
        /// Data block access to index.
        /// </summary>
        /// <param name="access">The data block access.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private int DataBlockAccessToIndex(Mifare1KDataBlockAccessConditions access)
        {
            switch (access)
            {
                case Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB:
                    return 0;
                case Mifare1KDataBlockAccessConditions.rAB:
                    return 1;
                case Mifare1KDataBlockAccessConditions.rAB_wB:
                    return 2;
                case Mifare1KDataBlockAccessConditions.rAB_wB_iB_drtAB:
                    return 3;
                case Mifare1KDataBlockAccessConditions.rAB_dtrAB:
                    return 4;
                case Mifare1KDataBlockAccessConditions.rB_wB:
                    return 5;
                case Mifare1KDataBlockAccessConditions.rB:
                    return 6;
                default:
                    return 7;
            }
        }

        /// <summary>
        /// Index to trailer block access.
        /// </summary>
        /// <param name="index">The trailer block access.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private Mifare1KTrailerBlockAccessConditions IndexToTrailerBlockAccess(int index)
        {
            switch (index)
            {
                case 0:
                    return Mifare1KTrailerBlockAccessConditions.kawA_abrA_kbrA_knwA;
                case 1:
                    return Mifare1KTrailerBlockAccessConditions.abwA_kbrA;
                case 2:
                    return Mifare1KTrailerBlockAccessConditions.kawB_abrAB_kbwB;
                case 3:
                    return Mifare1KTrailerBlockAccessConditions.abrAB;
                case 4:
                    return Mifare1KTrailerBlockAccessConditions.kawA_abrA_abwA_kbrA_kbwA;
                case 5:
                    return Mifare1KTrailerBlockAccessConditions.kawB_abrAB_abwB_kbwB;
                case 6:
                    return Mifare1KTrailerBlockAccessConditions.abrAB_abwB;
                default:
                    return Mifare1KTrailerBlockAccessConditions.abrAB2;
            }
        }

        /// <summary>
        /// Trailer block access to index.
        /// </summary>
        /// <param name="access">The trailer block access.</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private int TrailerBlockAccessToIndex(Mifare1KTrailerBlockAccessConditions access)
        {
            switch (access)
            {
                case Mifare1KTrailerBlockAccessConditions.kawA_abrA_kbrA_knwA:
                    return 0;
                case Mifare1KTrailerBlockAccessConditions.abwA_kbrA:
                    return 1;
                case Mifare1KTrailerBlockAccessConditions.kawB_abrAB_kbwB:
                    return 2;
                case Mifare1KTrailerBlockAccessConditions.abrAB:
                    return 3;
                case Mifare1KTrailerBlockAccessConditions.kawA_abrA_abwA_kbrA_kbwA:
                    return 4;
                case Mifare1KTrailerBlockAccessConditions.kawB_abrAB_abwB_kbwB:
                    return 5;
                case Mifare1KTrailerBlockAccessConditions.abrAB_abwB:
                    return 6;
                default:
                    return 7;
            }
        }


        /// <summary>
        /// Sectors the index of the access conditions to.
        /// </summary>
        /// <param name="conditions">The conditions.</param>
        /// <remarks></remarks>
        private void SectorAccessConditionsToIndex(SectorAccessConditions conditions)
        {
            Block0Access = DataBlockAccessToIndex(conditions.Block0);
            Block1Access = DataBlockAccessToIndex(conditions.Block1);
            Block2Access = DataBlockAccessToIndex(conditions.Block2);
            TrailerBlockAccess = TrailerBlockAccessToIndex(conditions.TrailerBlock);
        }

        /// <summary>
        /// Indexes to sector access conditions.
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        private SectorAccessConditions IndexToSectorAccessConditions()
        {
            return new SectorAccessConditions
            {
                Block0 = IndexToDataBlockAccess(Block0Access),
                Block1 = IndexToDataBlockAccess(Block1Access),
                Block2 = IndexToDataBlockAccess(Block2Access),
                TrailerBlock = IndexToTrailerBlockAccess(TrailerBlockAccess)
            };
        }


        /// <summary>
        /// Initializes a new instance of the Mifare1KVM class.
        /// </summary>
        /// <remarks></remarks>
        public Mifare1KVM()
        {
            ////if (IsInDesignMode)
            ////{
            ////    // Code runs in Blend --> create design time data.
            ////}
            ////else
            ////{
            ////    // Code runs "for real": Connect to service, etc...
            ////}
        }

        ////public override void Cleanup()
        ////{
        ////    // Clean own resources if needed

        ////    base.Cleanup();
        ////}
    }

    /// <summary>
    /// A block data access bits representation.
    /// </summary>
    /// <remarks></remarks>
    public class DataBlockAuthorizationLine
    {
        public string ReadAccess { get; set; }
        public string WriteAccess { get; set; }
        public string IncrementAccess { get; set; }
        public string DecrementAccess { get; set; }
        public string Application { get; set; }
    }

    /// <summary>
    /// A trailer block data access bits representation.
    /// </summary>
    /// <remarks></remarks>
    public class TrailerBlockAuthorizationLine
    {
        public string KeyAReadAccess { get; set; }
        public string KeyAWriteAccess { get; set; }
        public string KeyBReadAccess { get; set; }
        public string KeyBWriteAccess { get; set; }
        public string AccessBitReadAccess { get; set; }
        public string AccessBitWriteAccess { get; set; }

        public string Remark { get; set; }
    }
}